home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / js / calWcapSession.js < prev    next >
Text File  |  2008-01-16  |  55KB  |  1,305 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Sun Microsystems code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  * Sun Microsystems, Inc.
  19.  * Portions created by the Initial Developer are Copyright (C) 2007
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Daniel Boelzle <daniel.boelzle@sun.com>
  24.  *   Philipp Kewisch <mozilla@kewis.ch>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. function calWcapTimezone(tzProvider, tzid_, component_) {
  41.     this.wrappedJSObject = this;
  42.     this.provider = tzProvider;
  43.     this.component = component_;
  44.     this.tzid = tzid_;
  45.     this.isUTC = false;
  46.     this.isFloating = false;
  47.     this.latitude = "";
  48.     this.longitude = "";
  49. }
  50. calWcapTimezone.prototype = {
  51.     toString: function() {
  52.         // xxx todo remove: for some time, we want to know if a calITimezone object
  53.         //                  is handled as string...
  54.         ASSERT(false, "calWcapTimezone.toString!");
  55.         return this.component.toString();
  56.     }
  57. };
  58.  
  59. var g_openWcapSessions = {};
  60. function getWcapSessionFor(cal, uri) {
  61.     var contextId = cal.getProperty("shared_context");
  62.     if (!contextId) {
  63.         contextId = getUUID();
  64.         cal.setProperty("shared_context", contextId);
  65.     }
  66.     var session = g_openWcapSessions[contextId];
  67.     if (!session) {
  68.         session = new calWcapSession(contextId, uri);
  69.         g_openWcapSessions[contextId] = session;
  70.         // install a mandatory default calendar:
  71.         var defaultCal = cal;
  72.         for each (var regCal in session.getRegisteredCalendars()) {
  73.             if (regCal.isDefaultCalendar) {
  74.                 defaultCal = regCal;
  75.                 session.credentials.userId = defaultCal.getProperty("user_id");
  76.                 break;
  77.             }
  78.         }
  79.         session.defaultCalendar = defaultCal;
  80.     }
  81.     return session;
  82. }
  83.  
  84. function calWcapSession(contextId, thatUri) {
  85.     this.wrappedJSObject = this;
  86.     this.m_contextId = contextId;
  87.     this.m_loginQueue = [];
  88.  
  89.     this.m_uri = thatUri.clone();
  90.     this.m_sessionUri = thatUri.clone();
  91.     this.m_sessionUri.userPass = "";
  92.     log("new session", this);
  93.  
  94.     // listen for shutdown, being logged out:
  95.     var observerService = Components.classes["@mozilla.org/observer-service;1"]
  96.                                     .getService(Components.interfaces.nsIObserverService);
  97.     observerService.addObserver(this, "quit-application", false /* don't hold weakly */);
  98.     getCalendarManager().addObserver(this);
  99. }
  100. calWcapSession.prototype = {
  101.     m_ifaces: [ calIWcapSession,
  102.                 calIFreeBusyProvider,
  103.                 calICalendarSearchProvider,
  104.                 Components.interfaces.calITimezoneProvider,
  105.                 Components.interfaces.calICalendarManagerObserver,
  106.                 Components.interfaces.nsIInterfaceRequestor,
  107.                 Components.interfaces.nsIClassInfo,
  108.                 nsISupports ],
  109.     
  110.     // nsISupports:
  111.     QueryInterface: function calWcapSession_QueryInterface(iid) {
  112.         ensureIID(this.m_ifaces, iid); // throws
  113.         return this;
  114.     },
  115.     
  116.     // nsIClassInfo:
  117.     getInterfaces: function calWcapSession_getInterfaces(count)
  118.     {
  119.         count.value = this.m_ifaces.length;
  120.         return this.m_ifaces;
  121.     },
  122.     get classDescription() {
  123.         return calWcapCalendarModule.WcapSessionInfo.classDescription;
  124.     },
  125.     get contractID() {
  126.         return calWcapCalendarModule.WcapSessionInfo.contractID;
  127.     },
  128.     get classID() {
  129.         return calWcapCalendarModule.WcapSessionInfo.classID;
  130.     },
  131.     getHelperForLanguage:
  132.     function calWcapSession_getHelperForLanguage(language) { return null; },
  133.     implementationLanguage:
  134.     Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  135.     flags: 0,
  136.     
  137.     // nsIInterfaceRequestor:
  138.     getInterface: function calWcapSession_getInterface(iid, instance) {
  139.         if (iid.equals(Components.interfaces.nsIAuthPrompt)) {
  140.             // use the window watcher service to get a nsIAuthPrompt impl
  141.             return getWindowWatcher().getNewAuthPrompter(null);
  142.         }
  143.         else if (iid.equals(Components.interfaces.nsIPrompt)) {
  144.             // use the window watcher service to get a nsIPrompt impl
  145.             return getWindowWatcher().getNewPrompter(null);
  146.         }
  147.         Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  148.         return null;
  149.     },
  150.     
  151.     toString: function calWcapSession_toString(msg)
  152.     {
  153.         var str = ("context-id: " + this.m_contextId + ", uri: " + this.uri.spec);
  154.         if (this.credentials.userId) {
  155.             str += (", userId=" + this.credentials.userId);
  156.         }
  157.         if (!this.m_sessionId) {
  158.             str += (getIOService().offline ? ", offline" : ", not logged in");
  159.         }
  160.         return str;
  161.     },
  162.     notifyError: function calWcapSession_notifyError(err, suppressOnError)
  163.     {
  164.         if (this.defaultCalendar) {
  165.             this.defaultCalendar.notifyError_(err, this, suppressOnError);
  166.         } else {
  167.             logError("no default calendar!", this);
  168.             logError(err, this);
  169.         }
  170.     },
  171.  
  172.     // calITimezoneProvider:
  173.     m_serverTimezones: null,
  174.     get timezoneIds() {
  175.         var tzids = [];
  176.         tzids.push("floating");
  177.         tzids.push("UTC");
  178.         for (var tz in this.m_serverTimezones) {
  179.             tzids.push(tz.tzid);
  180.         }
  181.         return {
  182.             // nsIUTF8StringEnumerator:
  183.             m_index: 0,
  184.             getNext: function() {
  185.                 if (this.m_index >= tzids) {
  186.                     ASSERT(false, "calWcapSession::timezoneIds enumerator!");
  187.                     throw Components.results.NS_ERROR_UNEXPECTED;
  188.                 }
  189.                 return tzids[this.m_index++];
  190.             },
  191.             hasMoreElements: function() {
  192.                 return (this.m_index < tzids);
  193.             }
  194.         };
  195.     },
  196.     getTimezone: function calWcapSession_getTimezone(tzid) {
  197.         switch (tzid) {
  198.         case "floating":
  199.             return floating();
  200.         case "UTC":
  201.             return UTC();
  202.         default:
  203.             if (this.m_serverTimezones) {
  204.                 return this.m_serverTimezones[tzid];
  205.             }
  206.             return null;
  207.         }
  208.     },
  209.  
  210.     m_serverTimeDiff: null,
  211.     getServerTime: function calWcapSession_getServerTime(localTime)
  212.     {
  213.         if (this.m_serverTimeDiff === null) {
  214.             throw new Components.Exception(
  215.                 "early run into getServerTime()!",
  216.                 Components.results.NS_ERROR_NOT_AVAILABLE);
  217.         }
  218.         var ret = (localTime ? localTime.clone() : getTime());
  219.         ret.addDuration(this.m_serverTimeDiff);
  220.         return ret;
  221.     },
  222.     
  223.     m_sessionId: null,
  224.     m_loginQueue: null,
  225.     m_loginLock: false,
  226.     
  227.     getSessionId:
  228.     function calWcapSession_getSessionId(request, respFunc, timedOutSessionId)
  229.     {
  230.         if (getIOService().offline) {
  231.             log("in offline mode.", this);
  232.             respFunc(new Components.Exception(
  233.                          "The requested action could not be completed while the " +
  234.                          "networking library is in the offline state.",
  235.                          NS_ERROR_OFFLINE));
  236.             return;
  237.         }
  238.         
  239.         log("login queue lock: " + this.m_loginLock +
  240.             ", length: " + this.m_loginQueue.length, this);
  241.         
  242.         if (this.m_loginLock) {
  243.             this.m_loginQueue.push(respFunc);
  244.             log("login queue: " + this.m_loginQueue.length);
  245.         }
  246.         else {
  247.             if (this.m_sessionId && this.m_sessionId != timedOutSessionId) {
  248.                 respFunc(null, this.m_sessionId);
  249.                 return;
  250.             }
  251.             
  252.             this.m_loginLock = true;
  253.             log("locked login queue.", this);
  254.             this.m_sessionId = null; // invalidate for relogin
  255.             
  256.             if (timedOutSessionId) {
  257.                 log("reconnecting due to session timeout...", this);
  258.                 getFreeBusyService().removeProvider(this);
  259.                 getCalendarSearchService().removeProvider(this);
  260.             }
  261.             
  262.             var this_ = this;
  263.             this.getSessionId_(
  264.                 request,
  265.                 function getSessionId_resp_(err, sessionId) {
  266.                     log("getSessionId_resp_(): " + sessionId, this_);
  267.                     if (!err) {
  268.                         this_.m_sessionId = sessionId;
  269.                         getFreeBusyService().addProvider(this_);
  270.                         getCalendarSearchService().addProvider(this_);
  271.                     }
  272.                     
  273.                     var queue = this_.m_loginQueue;
  274.                     this_.m_loginLock = false;
  275.                     this_.m_loginQueue = [];
  276.                     log("unlocked login queue.", this_);
  277.  
  278.                     function getSessionId_exec(func) {
  279.                         try {
  280.                             func(err, sessionId);
  281.                         }
  282.                         catch (exc) { // unexpected
  283.                             this_.notifyError(exc);
  284.                         }
  285.                     }
  286.                     // answer first request:
  287.                     getSessionId_exec(respFunc);
  288.                     // and any remaining:
  289.                     queue.forEach(getSessionId_exec);
  290.                 });
  291.         }
  292.     },
  293.     
  294.     getSessionId_: function calWcapSession_getSessionId_(request, respFunc)
  295.     {
  296.         var this_ = this;
  297.         this.checkServerVersion(
  298.             request,
  299.             // probe whether server is accessible and responds:
  300.             function checkServerVersion_resp(err) {
  301.                 if (err) {
  302.                     respFunc(err);
  303.                     return;
  304.                 }
  305.                 // lookup password manager, then try login or prompt/login:
  306.                 log("attempting to get a session id for " + this_.sessionUri.spec, this_);
  307.                 
  308.                 if (!this_.sessionUri.schemeIs("https") &&
  309.                     !confirmInsecureLogin(this_.sessionUri)) {
  310.                     log("user rejected insecure login on " + this_.sessionUri.spec, this_);
  311.                     respFunc(new Components.Exception(
  312.                                  "Login failed. Invalid session ID.",
  313.                                  calIWcapErrors.WCAP_LOGIN_FAILED));
  314.                     return;
  315.                 }
  316.                 
  317.                 var outUser = { value: this_.credentials.userId };
  318.                 var outPW = { value: this_.credentials.pw };
  319.                 var outSavePW = { value: false };
  320.                 
  321.                 // pw mgr host names must not have a trailing slash
  322.                 var passwordManager =
  323.                     Components.classes["@mozilla.org/passwordmanager;1"]
  324.                               .getService(Components.interfaces.nsIPasswordManager);
  325.                 var pwHost = this_.uri.spec;
  326.                 if (pwHost[pwHost.length - 1] == '/') {
  327.                     pwHost = pwHost.substr(0, pwHost.length - 1);
  328.                 }
  329.                 if (outUser.value && !outPW.value) { // lookup pw manager
  330.                     log("looking in pw db for: " + pwHost, this_);
  331.                     try {
  332.                         var enumerator = passwordManager.enumerator;
  333.                         while (enumerator.hasMoreElements()) {
  334.                             var pwEntry = enumerator.getNext().QueryInterface(
  335.                                 Components.interfaces.nsIPassword);
  336.                             if (LOG_LEVEL > 1) {
  337.                                 log("pw entry:\n\thost=" + pwEntry.host +
  338.                                     "\n\tuser=" + pwEntry.user, this_);
  339.                             }
  340.                             if ((pwEntry.host == pwHost) &&
  341.                                 (pwEntry.user == outUser.value)){
  342.                                 // found an entry matching URI:
  343.                                 outPW.value = pwEntry.password;
  344.                                 log("password entry found for host " + pwHost +
  345.                                     "\nuser is " + outUser.value, this_);
  346.                                 break;
  347.                             }
  348.                         }
  349.                     }
  350.                     catch (exc) { // just log error
  351.                         logError("[password manager lookup] " + errorToString(exc), this_);
  352.                     }
  353.                 }
  354.  
  355.                 function promptAndLoginLoop_resp(err, sessionId) {
  356.                     if (checkErrorCode(err, calIWcapErrors.WCAP_LOGIN_FAILED)) {
  357.                         log("prompting for [user/]pw...", this_);
  358.                         var prompt = getWindowWatcher().getNewPrompter(null);
  359.                         var bAck;
  360.                         if (this_.credentials.userId) { // fixed user id, prompt for password only:
  361.                             bAck = prompt.promptPassword(
  362.                                 calGetString("wcap", "loginDialog.label"),
  363.                                 calGetString("wcap", "loginDialogPasswordOnly.text",
  364.                                              [outUser.value, this_.sessionUri.hostPort]),
  365.                                 outPW,
  366.                                 (getPref("signon.rememberSignons", true)
  367.                                  ? calGetString("wcap", "loginDialog.check.text") : null),
  368.                                 outSavePW);
  369.                         } else {
  370.                             bAck = prompt.promptUsernameAndPassword(
  371.                                 calGetString("wcap", "loginDialog.label"),
  372.                                 calGetString("wcap", "loginDialog.text",
  373.                                              [this_.sessionUri.hostPort]),
  374.                                 outUser, outPW,
  375.                                 (getPref("signon.rememberSignons", true)
  376.                                  ? calGetString("wcap", "loginDialog.check.text") : null),
  377.                                 outSavePW);
  378.                         }
  379.                         if (bAck) {
  380.                             this_.login(request, promptAndLoginLoop_resp,
  381.                                         outUser.value, outPW.value);
  382.                         }
  383.                         else {
  384.                             log("login prompt cancelled.", this_);
  385.                             respFunc(new Components.Exception(
  386.                                          "Login failed. Invalid session ID.",
  387.                                          calIWcapErrors.WCAP_LOGIN_FAILED));
  388.                         }
  389.                     }
  390.                     else if (err)
  391.                         respFunc(err);
  392.                     else {
  393.                         if (outSavePW.value) {
  394.                             // so try to remove old pw from db first:
  395.                             try {
  396.                                 passwordManager.removeUser(pwHost, outUser.value);
  397.                                 log("removed from pw db: " + pwHost, this_);
  398.                             }
  399.                             catch (exc) {
  400.                             }
  401.                             try { // to save pw under session uri:
  402.                                 passwordManager.addUser(pwHost, outUser.value, outPW.value);
  403.                                 log("added to pw db: " + pwHost, this_);
  404.                             }
  405.                             catch (exc) {
  406.                                 logError("[adding pw to db] " + errorToString(exc), this_);
  407.                             }
  408.                         }
  409.                         this_.credentials.userId = outUser.value;
  410.                         this_.credentials.pw = outPW.value;
  411.                         this_.setupSession(sessionId,
  412.                                            request,
  413.                                            function setupSession_resp(err) {
  414.                                                respFunc(err, sessionId);
  415.                                            });
  416.                     }
  417.                 }
  418.                     
  419.                 if (outPW.value) {
  420.                     this_.login(request, promptAndLoginLoop_resp,
  421.                                 outUser.value, outPW.value);
  422.                 }
  423.                 else {
  424.                     promptAndLoginLoop_resp(calIWcapErrors.WCAP_LOGIN_FAILED);
  425.                 }
  426.             });
  427.     },
  428.     
  429.     login: function calWcapSession_login(request, respFunc, user, pw)
  430.     {
  431.         var this_ = this;
  432.         issueNetworkRequest(
  433.             request,
  434.             function netResp(err, str) {
  435.                 var sessionId;
  436.                 try {
  437.                     if (err)
  438.                         throw err;
  439.                     // currently, xml parsing at an early stage during
  440.                     // process startup does not work reliably, so use
  441.                     // libical parsing for now:
  442.                     var icalRootComp = stringToIcal(this_, str);
  443.                     var prop = icalRootComp.getFirstProperty("X-NSCP-WCAP-SESSION-ID");
  444.                     if (!prop) {
  445.                         throw new Components.Exception(
  446.                             "missing X-NSCP-WCAP-SESSION-ID in\n" + str);
  447.                     }
  448.                     sessionId = prop.value;
  449.                     log("login succeeded: " + sessionId, this_);
  450.                 }
  451.                 catch (exc) {
  452.                     err = exc;
  453.                     if (checkErrorCode(err, calIWcapErrors.WCAP_LOGIN_FAILED)) {
  454.                         log("error: " + errorToString(exc), this_); // log login failure
  455.                     }
  456.                     else if (getErrorModule(err) == NS_ERROR_MODULE_NETWORK) {
  457.                         // server seems unavailable:
  458.                         err = new Components.Exception(
  459.                             calGetString( "wcap", "accessingServerFailedError.text",
  460.                                           [this_.sessionUri.hostPort]),
  461.                             exc);
  462.                     }
  463.                 }
  464.                 respFunc(err, sessionId);
  465.             },
  466.             this_.sessionUri.spec + "login.wcap?fmt-out=text%2Fcalendar&user=" +
  467.             encodeURIComponent(user) + "&password=" + encodeURIComponent(pw),
  468.             false /* no logging */);
  469.     },
  470.     
  471.     logout: function calWcapSession_logout(listener)
  472.     {
  473.         var this_ = this;
  474.         var request = new calWcapRequest(
  475.             function logout_resp(request, err) {
  476.                 if (err)
  477.                     logError(err, this_);
  478.                 else
  479.                     log("logout succeeded.", this_);
  480.                 if (listener)
  481.                     listener.onResult(request, null);
  482.             },
  483.             log("logout", this));
  484.         
  485.         var url = null;
  486.         if (this.m_sessionId) {
  487.             log("attempting to log out...", this);
  488.             // although io service's offline flag is already
  489.             // set BEFORE notification
  490.             // (about to go offline, nsIOService.cpp).
  491.             // WTF.
  492.             url = (this.sessionUri.spec + "logout.wcap?fmt-out=text%2Fxml&id=" + this.m_sessionId);
  493.             this.m_sessionId = null;
  494.             getFreeBusyService().removeProvider(this);
  495.             getCalendarSearchService().removeProvider(this);
  496.         }
  497.         this.m_credentials = null;
  498.         
  499.         if (url) {
  500.             issueNetworkRequest(
  501.                 request,
  502.                 function netResp(err, str) {
  503.                     if (err)
  504.                         throw err;
  505.                     stringToXml(this_, str, -1 /* logout successfull */);
  506.                 }, url);
  507.         }
  508.         else {
  509.             request.execRespFunc();
  510.         }
  511.         return request;
  512.     },
  513.     
  514.     checkServerVersion: function calWcapSession_checkServerVersion(request, respFunc)
  515.     {
  516.         // currently, xml parsing at an early stage during process startup
  517.         // does not work reliably, so use libical:
  518.         var this_ = this;
  519.         issueNetworkRequest(
  520.             request,
  521.             function netResp(err, str) {
  522.                 try {
  523.                     var icalRootComp;
  524.                     if (!err) {
  525.                         try {
  526.                             icalRootComp = stringToIcal(this_, str);
  527.                         }
  528.                         catch (exc) {
  529.                             err = exc;
  530.                         }
  531.                     }
  532.                     if (err) {
  533.                         if (checkErrorCode(err, calIErrors.OPERATION_CANCELLED)) {
  534.                             throw err;
  535.                         } else { // soft error; request denied etc.
  536.                                  // map into localized message:
  537.                             throw new Components.Exception(
  538.                                 calGetString("wcap", "accessingServerFailedError.text",
  539.                                              [this_.sessionUri.hostPort]),
  540.                                 calIWcapErrors.WCAP_LOGIN_FAILED);
  541.                         }
  542.                     }
  543.                     var prop = icalRootComp.getFirstProperty("X-NSCP-WCAPVERSION");
  544.                     if (!prop)
  545.                         throw new Components.Exception("missing X-NSCP-WCAPVERSION!");
  546.                     var wcapVersion = parseInt(prop.value);
  547.                     if (wcapVersion < 3) {
  548.                         var strVers = prop.value;
  549.                         var vars = [this_.sessionUri.hostPort];
  550.                         prop = icalRootComp.getFirstProperty("PRODID");
  551.                         vars.push(prop ? prop.value : "<unknown>");
  552.                         prop = icalRootComp.getFirstProperty("X-NSCP-SERVERVERSION");
  553.                         vars.push(prop ? prop.value : "<unknown>");
  554.                         vars.push(strVers);
  555.                         
  556.                         var prompt = getWindowWatcher().getNewPrompter(null);
  557.                         var labelText = calGetString(
  558.                             "wcap", "insufficientWcapVersionConfirmation.label");
  559.                         if (!prompt.confirm(
  560.                                 labelText,
  561.                                 calGetString("wcap", "insufficientWcapVersionConfirmation.text", vars))) {
  562.                             throw new Components.Exception(labelText,
  563.                                                            calIWcapErrors.WCAP_LOGIN_FAILED);
  564.                         }
  565.                     }
  566.                 }
  567.                 catch (exc) {
  568.                     err = exc;
  569.                 }
  570.                 respFunc(err);
  571.             },
  572.             this_.sessionUri.spec + "version.wcap?fmt-out=text%2Fcalendar");
  573.     },
  574.  
  575.     setupSession:
  576.     function calWcapSession_setupSession(sessionId, request_, respFunc)
  577.     {
  578.         var this_ = this;
  579.         var request = new calWcapRequest(
  580.             function setupSession_resp(request_, err) {
  581.                 log("setupSession_resp finished: " + errorToString(err), this_);
  582.                 respFunc(err);
  583.             },
  584.             log("setupSession", this));
  585.         request_.attachSubRequest(request);
  586.         
  587.         request.lockPending();
  588.         try {
  589.             var this_ = this;
  590.             this.issueNetworkRequest_(
  591.                 request,
  592.                 function userprefs_resp(err, data) {
  593.                     if (err)
  594.                         throw err;
  595.                     this_.credentials.userPrefs = data;
  596.                     log("installed user prefs.", this_);
  597.                     
  598.                     // get calprops for all registered calendars:                        
  599.                     var cals = this_.getRegisteredCalendars(true);
  600.  
  601.                     var calprops_resp = null;
  602.                     var defaultCal = this_.defaultCalendar;
  603.                     if (defaultCal && cals[defaultCal.calId] && // default calendar is registered
  604.                         getPref("calendar.wcap.subscriptions", true) &&
  605.                         !defaultCal.getProperty("subscriptions_registered")) {
  606.                         
  607.                         var hasSubscriptions = false;
  608.                         // post register subscribed calendars:
  609.                         var list = this_.getUserPreferences("X-NSCP-WCAP-PREF-icsSubscribed");
  610.                         for each (var item in list) {
  611.                             var ar = item.split(',');
  612.                             // ',', '$' are not encoded. ',' can be handled here. WTF.
  613.                             for each (var a in ar) {
  614.                                 var dollar = a.indexOf('$');
  615.                                 if (dollar >= 0) {
  616.                                     var calId = a.substring(0, dollar);
  617.                                     if (calId != this_.defaultCalId) {
  618.                                         cals[calId] = null;
  619.                                         hasSubscriptions = true;
  620.                                     }
  621.                                 }
  622.                             }
  623.                         }
  624.                         
  625.                         if (hasSubscriptions) {
  626.                             calprops_resp = function(cal) {
  627.                                 if (cal.isDefaultCalendar) {
  628.                                     // tweak name:
  629.                                     cal.setProperty("name", cal.displayName);
  630.                                 }
  631.                                 else {
  632.                                     log("registering subscribed calendar: " + cal.calId, this_);
  633.                                     getCalendarManager().registerCalendar(cal);
  634.                                 }
  635.                             }
  636.                             // do only once:
  637.                             defaultCal.setProperty("account_name", defaultCal.name);
  638.                             defaultCal.setProperty("subscriptions_registered", true);
  639.                         }
  640.                     }
  641.  
  642.                     if (!defaultCal.getProperty("user_id")) { // nail once:
  643.                         defaultCal.setProperty("user_id", this_.credentials.userId);
  644.                     }
  645.  
  646.                     if (getPref("calendar.wcap.no_get_calprops", false)) {
  647.                         // hack around the get/search calprops mess:
  648.                         this_.installCalProps_search_calprops(calprops_resp, sessionId, cals, request);
  649.                     }
  650.                     else {
  651.                         this_.installCalProps_get_calprops(calprops_resp, sessionId, cals, request);
  652.                     }
  653.                 },
  654.                 stringToXml, "get_userprefs",
  655.                 "&fmt-out=text%2Fxml&userid=" + encodeURIComponent(this.credentials.userId),
  656.                 sessionId);
  657.             this.installServerTimeDiff(sessionId, request);
  658.             this.installServerTimezones(sessionId, request);
  659.         }
  660.         finally {
  661.             request.unlockPending();
  662.         }
  663.     },
  664.     
  665.     installCalProps_get_calprops:
  666.     function calWcapSession_installCalProps_get_calprops(respFunc, sessionId, cals, request)
  667.     {
  668.         var this_ = this;
  669.         function calprops_resp(err, data) {
  670.             if (err)
  671.                 throw err;
  672.             // string to xml converter func without WCAP errno check:
  673.             if (!data || data.length == 0) { // assuming time-out
  674.                 throw new Components.Exception("Login failed. Invalid session ID.",
  675.                                                calIWcapErrors.WCAP_LOGIN_FAILED);
  676.             }
  677.             var xml = getDomParser().parseFromString(data, "text/xml");
  678.             var nodeList = xml.getElementsByTagName("iCal");
  679.             for (var i = 0; i < nodeList.length; ++i) {
  680.                 try {
  681.                     var node = nodeList.item(i);
  682.                     checkWcapXmlErrno(node);
  683.                     var ar = filterXmlNodes("X-NSCP-CALPROPS-RELATIVE-CALID", node);
  684.                     if (ar.length > 0) {
  685.                         var calId = ar[0];
  686.                         var cal = cals[calId];
  687.                         if (cal === null) {
  688.                             cal = new calWcapCalendar(this_);
  689.                             var uri = this_.uri.clone();
  690.                             uri.path += ("?calid=" + encodeURIComponent(calId));
  691.                             cal.uri = uri;
  692.                         }
  693.                         if (cal) {
  694.                             cal.m_calProps = node;
  695.                             if (respFunc) {
  696.                                 respFunc(cal);
  697.                             }
  698.                         }
  699.                     }
  700.                 }
  701.                 catch (exc) { // ignore but log any errors on subscribed calendars:
  702.                     logError(exc, this_);
  703.                 }
  704.             }
  705.         }
  706.  
  707.         var calidParam = "";
  708.         for (var calId in cals) {
  709.             if (calidParam.length > 0)
  710.                 calidParam += ";";
  711.             calidParam += encodeURIComponent(calId);
  712.         }
  713.         this_.issueNetworkRequest_(request, calprops_resp,
  714.                                    null, "get_calprops",
  715.                                    "&fmt-out=text%2Fxml&calid=" + calidParam,
  716.                                    sessionId);
  717.     },
  718.  
  719.     installCalProps_search_calprops:
  720.     function calWcapSession_installCalProps_search_calprops(respFunc, sessionId, cals, request)
  721.     {
  722.         var this_ = this;
  723.         var retrievedCals = {};
  724.         var issuedSearchRequests = {};
  725.         for (var calId in cals) {
  726.             if (!retrievedCals[calId]) {
  727.                 var listener = {
  728.                     onResult: function search_onResult(request, result) {
  729.                         try {
  730.                             if (!Components.isSuccessCode(request.status))
  731.                                 throw request.status;
  732.                             if (result.length < 1)
  733.                                 throw Components.results.NS_ERROR_UNEXPECTED;
  734.                             for each (var cal in result) {
  735.                                 // user may have dangling users referred in his subscription list, so
  736.                                 // retrieve each by each, don't break:
  737.                                 try {
  738.                                     var calId = cal.calId;
  739.                                     if ((cals[calId] !== undefined) && !retrievedCals[calId]) {
  740.                                         retrievedCals[calId] = cal;
  741.                                         if (respFunc) {
  742.                                             respFunc(cal);
  743.                                         }
  744.                                     }
  745.                                 }
  746.                                 catch (exc) { // ignore but log any errors on subscribed calendars:
  747.                                     logError(exc, this_);
  748.                                 }
  749.                             }
  750.                         }
  751.                         catch (exc) { // ignore but log any errors on subscribed calendars:
  752.                             logError(exc, this_);
  753.                         }
  754.                     }
  755.                 };
  756.                 
  757.                 var colon = calId.indexOf(':');
  758.                 if (colon >= 0) // searching for secondary calendars doesn't work. WTF.
  759.                     calId = calId.substring(0, colon);
  760.                 if (!issuedSearchRequests[calId]) {
  761.                     issuedSearchRequests[calId] = true;
  762.                     this.searchForCalendars(
  763.                         calId, calICalendarSearchProvider.HINT_EXACT_MATCH, 20, listener);
  764.                 }
  765.             }
  766.         }
  767.     },
  768.  
  769.     installServerTimeDiff:
  770.     function calWcapSession_installServerTimeDiff(sessionId, request)
  771.     {
  772.         var this_ = this;
  773.         this.issueNetworkRequest_(
  774.             request,
  775.             function netResp(err, data) {
  776.                 if (err)
  777.                     throw err;
  778.                 // xxx todo: think about
  779.                 // assure that locally calculated server time is smaller
  780.                 // than the current (real) server time:
  781.                 var localTime = getTime();
  782.                 var serverTime = getDatetimeFromIcalProp(
  783.                     data.getFirstProperty("X-NSCP-WCAPTIME"));
  784.                 this_.m_serverTimeDiff = serverTime.subtractDate(localTime);
  785.                 log("server time diff is: " + this_.m_serverTimeDiff, this_);
  786.             },
  787.             stringToIcal, "gettime", "&fmt-out=text%2Fcalendar",
  788.             sessionId);
  789.     },
  790.     
  791.     installServerTimezones:
  792.     function calWcapSession_installServerTimezones(sessionId, request)
  793.     {
  794.         this.m_serverTimezones = {};
  795.         var this_ = this;
  796.         this_.issueNetworkRequest_(
  797.             request,
  798.             function netResp(err, data) {
  799.                 if (err)
  800.                     throw err;
  801.                 var tzids = [];
  802.                 forEachIcalComponent(
  803.                     data, "VTIMEZONE",
  804.                     function eachComp(subComp) {
  805.                         try {
  806.                             var tzid = subComp.getFirstProperty("TZID").value;
  807.                             this_.m_serverTimezones[tzid] = new calWcapTimezone(this_, tzid, subComp);
  808.                         }
  809.                         catch (exc) { // ignore but errors:
  810.                             logError(exc, this_);
  811.                         }
  812.                     });
  813.                 log("installed timezones.", this_);
  814.             },
  815.             stringToIcal, "get_all_timezones", "&fmt-out=text%2Fcalendar",
  816.             sessionId);
  817.     },
  818.     
  819.     getCommandUrl: function calWcapSession_getCommandUrl(wcapCommand, params, sessionId)
  820.     {
  821.         var url = this.sessionUri.spec;
  822.         url += (wcapCommand + ".wcap?appid=mozilla-calendar&id=");
  823.         url += sessionId;
  824.         url += params;
  825.         return url;
  826.     },
  827.  
  828.     issueNetworkRequest: function calWcapSession_issueNetworkRequest(
  829.         request, respFunc, dataConvFunc, wcapCommand, params)
  830.     {
  831.         var this_ = this;
  832.         function getSessionId_resp(err, sessionId) {
  833.             if (err)
  834.                 request.execSubRespFunc(respFunc, err);
  835.             else {
  836.                 // else have session uri and id:
  837.                 this_.issueNetworkRequest_(
  838.                     request,
  839.                     function issueNetworkRequest_resp(err, data) {
  840.                         // timeout?
  841.                         if (checkErrorCode(err, calIWcapErrors.WCAP_LOGIN_FAILED)) {
  842.                             // try again:
  843.                             this_.getSessionId(
  844.                                 request,
  845.                                 getSessionId_resp,
  846.                                 sessionId/* (old) timed-out session */);
  847.                             return;
  848.                         }
  849.                         request.execSubRespFunc(respFunc, err, data);
  850.                     },
  851.                     dataConvFunc, wcapCommand, params, sessionId);
  852.             }
  853.         }
  854.         this.getSessionId(request, getSessionId_resp);
  855.     },
  856.     
  857.     issueNetworkRequest_: function calWcapSession_issueNetworkRequest_(
  858.         request, respFunc, dataConvFunc, wcapCommand, params, sessionId)
  859.     {
  860.         var url = this.getCommandUrl(wcapCommand, params, sessionId);
  861.         var this_ = this;
  862.         issueNetworkRequest(
  863.             request,
  864.             function netResp(err, str) {
  865.                 var data;
  866.                 if (!err) {
  867.                     try {
  868.                         if (dataConvFunc)
  869.                             data = dataConvFunc(this_, str);
  870.                         else
  871.                             data = str;
  872.                     }
  873.                     catch (exc) {
  874.                         err = exc;
  875.                     }
  876.                 }
  877.                 request.execSubRespFunc(respFunc, err, data);
  878.             }, url);
  879.     },
  880.     
  881.     m_credentials: null,
  882.     get credentials() {
  883.         if (!this.m_credentials) {
  884.             this.m_credentials = {};
  885.         }
  886.         return this.m_credentials;
  887.     },
  888.     
  889.     // calIWcapSession:
  890.  
  891.     m_contextId: null,
  892.     m_uri: null,
  893.     m_sessionUri: null,
  894.     get uri() { return this.m_uri; },
  895.     get sessionUri() { return this.m_sessionUri; },
  896.     
  897.     get userId() { return this.credentials.userId; },
  898.     
  899.     get defaultCalId() {
  900.         var list = this.getUserPreferences("X-NSCP-WCAP-PREF-icsCalendar");
  901.         var id = null;
  902.         for each (var item in list) {
  903.             if (item.length > 0) {
  904.                 id = item;
  905.                 break;
  906.             }
  907.         }
  908.         return (id ? id : this.credentials.userId);
  909.     },
  910.     
  911.     get isLoggedIn() {
  912.         return (this.m_sessionId != null);
  913.     },
  914.     
  915.     defaultCalendar: null,
  916.     
  917.     belongsTo: function calWcapSession_belongsTo(cal) {
  918.         try {
  919.             // xxx todo hack to get the unwrapped wcap calendar instance:
  920.             cal = cal.getProperty("private.wcapCalendar")
  921.                      .QueryInterface(calIWcapCalendar).wrappedJSObject;
  922.             if (cal && (cal.session.m_contextId == this.m_contextId)) {
  923.                 return cal;
  924.             }
  925.         }
  926.         catch (exc) {
  927.         }
  928.         return null;
  929.     },
  930.  
  931.     getRegisteredCalendars: function calWcapSession_getRegisteredCalendars(asAssocObj) {
  932.         var registeredCalendars = (asAssocObj ? {} : []);
  933.         var cals = getCalendarManager().getCalendars({});
  934.         for each (var cal in cals) {
  935.             cal = this.belongsTo(cal);
  936.             if (cal) {
  937.                 if (asAssocObj) {
  938.                     registeredCalendars[cal.calId] = cal;
  939.                 } else {
  940.                     registeredCalendars.push(cal);
  941.                 }
  942.             }
  943.         }
  944.         return registeredCalendars;
  945.     },
  946.  
  947.     getUserPreferences: function calWcapSession_getUserPreferences(prefName) {
  948.         var prefs = filterXmlNodes(prefName, this.credentials.userPrefs);
  949.         return prefs;
  950.     },
  951.     
  952.     get defaultAlarmStart() {
  953.         var alarmStart = null;
  954.         var ar = this.getUserPreferences("X-NSCP-WCAP-PREF-ceDefaultAlarmStart");
  955.         if (ar.length > 0 && ar[0].length > 0) {
  956.             // workarounding cs duration bug, missing "T":
  957.             var dur = ar[0].replace(/(^P)(\d+[HMS]$)/, "$1T$2");
  958.             alarmStart = new CalDuration();
  959.             alarmStart.icalString = dur;
  960.             alarmStart.isNegative = !alarmStart.isNegative;
  961.         }
  962.         return alarmStart;
  963.     },
  964.     
  965.     getDefaultAlarmEmails: function calWcapSession_getDefaultAlarmEmails(out_count)
  966.     {
  967.         var ret = [];
  968.         var ar = this.getUserPreferences("X-NSCP-WCAP-PREF-ceDefaultAlarmEmail");
  969.         if (ar.length > 0 && ar[0].length > 0) {
  970.             for each (var i in ar) {
  971.                 ret = ret.concat( i.split(/[;,]/).map(trimString) );
  972.             }
  973.         }
  974.         out_count.value = ret.length;
  975.         return ret;
  976.     },
  977.  
  978.     // calICalendarSearchProvider:
  979.     searchForCalendars:
  980.     function calWcapSession_searchForCalendars(searchString, hints, maxResults, listener)
  981.     {
  982.         var this_ = this;
  983.         var request = new calWcapRequest(
  984.             function searchForCalendars_resp(request, err, data) {
  985.                 if (err && !checkErrorCode(err, calIErrors.OPERATION_CANCELLED)) {
  986.                     this_.notifyError(err);
  987.                 }
  988.                 if (listener) {
  989.                     listener.onResult(request, data);
  990.                 }
  991.             },
  992.             log("searchForCalendars, searchString=" + searchString, this));
  993.         
  994.         try {
  995.             var registeredCalendars = this.getRegisteredCalendars(true);
  996.  
  997.             var params = ("&fmt-out=text%2Fxml&search-string=" +
  998.                           encodeURIComponent(searchString));
  999.             if (maxResults > 0) {
  1000.                 params += ("&maxResults=" + maxResults);
  1001.             }
  1002.             params += ("&name=1&calid=1&primaryOwner=1&searchOpts=" +
  1003.                        ((hints & calICalendarSearchProvider.HINT_EXACT_MATCH) ? "3" : "0"));
  1004.  
  1005.             this.issueNetworkRequest(
  1006.                 request,
  1007.                 function searchForCalendars_netResp(err, data) {
  1008.                     if (err)
  1009.                         throw err;
  1010.                     // string to xml converter func without WCAP errno check:
  1011.                     if (!data || data.length == 0) { // assuming time-out
  1012.                         throw new Components.Exception("Login failed. Invalid session ID.",
  1013.                                                        calIWcapErrors.WCAP_LOGIN_FAILED);
  1014.                     }
  1015.                     var xml = getDomParser().parseFromString(data, "text/xml");
  1016.                     var ret = [];
  1017.                     var nodeList = xml.getElementsByTagName("iCal");
  1018.                     for ( var i = 0; i < nodeList.length; ++i ) {
  1019.                         var node = nodeList.item(i);
  1020.                         try {
  1021.                             checkWcapXmlErrno(node);
  1022.                             var ar = filterXmlNodes("X-NSCP-CALPROPS-RELATIVE-CALID", node);
  1023.                             if (ar.length > 0) {
  1024.                                 var calId = ar[0];
  1025.                                 var cal = registeredCalendars[calId];
  1026.                                 if (cal) {
  1027.                                     cal.m_calProps = node; // update calprops
  1028.                                 }
  1029.                                 else {
  1030.                                     cal = new calWcapCalendar(this_, node);
  1031.                                     var uri = this_.uri.clone();
  1032.                                     uri.path += ("?calid=" + encodeURIComponent(calId));
  1033.                                     cal.uri = uri;
  1034.                                 }
  1035.                                 ret.push(cal);
  1036.                             }
  1037.                         }
  1038.                         catch (exc) {
  1039.                             switch (getResultCode(exc)) {
  1040.                             case calIWcapErrors.WCAP_NO_ERRNO: // workaround
  1041.                             case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
  1042.                                 log("searchForCalendars_netResp() ignored error: " +
  1043.                                     errorToString(exc), this_);
  1044.                                 break;
  1045.                             default:
  1046.                                 this_.notifyError(exc);
  1047.                                 break;
  1048.                             }
  1049.                         }
  1050.                     }
  1051.                     log("search done. number of found calendars: " + ret.length, this_);
  1052.                     request.execRespFunc(null, ret);
  1053.                 },
  1054.                 null, "search_calprops", params);
  1055.         }
  1056.         catch (exc) {
  1057.             request.execRespFunc(exc);
  1058.         }
  1059.         return request;
  1060.     },
  1061.  
  1062.     // calIFreeBusyProvider:
  1063.     getFreeBusyIntervals: function calWcapCalendar_getFreeBusyIntervals(
  1064.         calId, rangeStart, rangeEnd, busyTypes, listener)
  1065.     {
  1066.         // assure DATETIMEs:
  1067.         if (rangeStart && rangeStart.isDate) {
  1068.             rangeStart = rangeStart.clone();
  1069.             rangeStart.isDate = false;
  1070.         }
  1071.         if (rangeEnd && rangeEnd.isDate) {
  1072.             rangeEnd = rangeEnd.clone();
  1073.             rangeEnd.isDate = false;
  1074.         }
  1075.         var zRangeStart = getIcalUTC(rangeStart);
  1076.         var zRangeEnd = getIcalUTC(rangeEnd);
  1077.         
  1078.         var this_ = this;
  1079.         var request = new calWcapRequest(
  1080.             function _resp(request, err, data) {
  1081.                 var rc = getResultCode(err);
  1082.                 switch (rc) {
  1083.                 case calIWcapErrors.WCAP_NO_ERRNO: // workaround
  1084.                 case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
  1085.                 case calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST:
  1086.                     log("getFreeBusyIntervals_resp() error: " + errorToString(err), this_);
  1087.                     break;
  1088.                 default:
  1089.                     if (!Components.isSuccessCode(rc))
  1090.                         this_.notifyError(err);
  1091.                     break;
  1092.                 }
  1093.                 if (listener)
  1094.                     listener.onResult(request, data);
  1095.             },
  1096.             log("getFreeBusyIntervals():\n\tcalId=" + calId +
  1097.                 "\n\trangeStart=" + zRangeStart + ",\n\trangeEnd=" + zRangeEnd, this));
  1098.         
  1099.         try {
  1100.             var params = ("&calid=" + encodeURIComponent(calId));
  1101.             params += ("&busyonly=" + ((busyTypes & calIFreeBusyInterval.FREE) ? "0" : "1"));
  1102.             params += ("&dtstart=" + zRangeStart);
  1103.             params += ("&dtend=" + zRangeEnd);
  1104.             params += "&fmt-out=text%2Fxml";
  1105.  
  1106.             // cannot use stringToXml here, because cs 6.3 returns plain nothing
  1107.             // on invalid user freebusy requests. WTF.
  1108.             function stringToXml_(session, data) {
  1109.                 if (!data || data.length == 0) { // assuming invalid user
  1110.                     throw new Components.Exception(
  1111.                         wcapErrorToString(calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST),
  1112.                         calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST);
  1113.                 }
  1114.                 return stringToXml(session, data);
  1115.             }
  1116.             this.issueNetworkRequest(
  1117.                 request,
  1118.                 function net_resp(err, xml) {
  1119.                     if (err)
  1120.                         throw err;
  1121.                     if (LOG_LEVEL > 0) {
  1122.                         log("getFreeBusyIntervals net_resp(): " +
  1123.                             getWcapRequestStatusString(xml), this_);
  1124.                     }
  1125.                     if (listener) {
  1126.                         var ret = [];
  1127.                         var nodeList = xml.getElementsByTagName("FB");
  1128.                         
  1129.                         var fbTypeMap = {};
  1130.                         fbTypeMap["FREE"] = calIFreeBusyInterval.FREE;
  1131.                         fbTypeMap["BUSY"] = calIFreeBusyInterval.BUSY;
  1132.                         fbTypeMap["BUSY-UNAVAILABLE"] = calIFreeBusyInterval.BUSY_UNAVAILABLE;
  1133.                         fbTypeMap["BUSY-TENTATIVE"] = calIFreeBusyInterval.BUSY_TENTATIVE;
  1134.                         
  1135.                         for (var i = 0; i < nodeList.length; ++i) {
  1136.                             var node = nodeList.item(i);
  1137.                             var fbType = fbTypeMap[node.attributes.getNamedItem("FBTYPE").nodeValue];
  1138.                             if (fbType & busyTypes) {
  1139.                                 var str = node.textContent;
  1140.                                 var slash = str.indexOf('/');
  1141.                                 var period = new CalPeriod();
  1142.                                 period.start = getDatetimeFromIcalString(str.substr(0, slash));
  1143.                                 period.end = getDatetimeFromIcalString(str.substr(slash + 1));
  1144.                                 period.makeImmutable();
  1145.                                 var fbInterval = {
  1146.                                     QueryInterface: function fbInterval_QueryInterface(iid) {
  1147.                                         ensureIID([calIFreeBusyInterval, nsISupports], iid);
  1148.                                         return this;
  1149.                                     },
  1150.                                     calId: calId,
  1151.                                     interval: period,
  1152.                                     freeBusyType: fbType
  1153.                                 };
  1154.                                 ret.push(fbInterval);
  1155.                             }
  1156.                         }
  1157.                         request.execRespFunc(null, ret);
  1158.                     }
  1159.                 },
  1160.                 stringToXml_, "get_freebusy", params);
  1161.         }
  1162.         catch (exc) {
  1163.             request.execRespFunc(exc);
  1164.         }
  1165.         return request;
  1166.     },
  1167.     
  1168.     // nsIObserver:
  1169.     observe: function calWcapSession_observer(subject, topic, data)
  1170.     {
  1171.         log("observing: " + topic + ", data: " + data, this);
  1172.         if (topic == "quit-application") {
  1173.             g_bShutdown = true;
  1174.             this.logout(null);
  1175.             // xxx todo: valid upon notification?
  1176.             getCalendarManager().removeObserver(this);
  1177.             var observerService = Components.classes["@mozilla.org/observer-service;1"]
  1178.                                             .getService(Components.interfaces.nsIObserverService);
  1179.             observerService.removeObserver(this, "quit-application");
  1180.         }
  1181.     },
  1182.     
  1183.     // calICalendarManagerObserver:
  1184.     
  1185.     // called after the calendar is registered
  1186.     onCalendarRegistered: function calWcapSession_onCalendarRegistered(cal)
  1187.     {
  1188.         try {
  1189.             // make sure the calendar belongs to this session:
  1190.             if (this.belongsTo(cal)) {
  1191.  
  1192.                 function assureDefault(pref, val) {
  1193.                     if (cal.getProperty(pref) === null) {
  1194.                         cal.setProperty(pref, val);
  1195.                     }
  1196.                 }
  1197.                 
  1198.                 assureDefault("shared_context", this.m_contextId);
  1199.                 assureDefault("name", cal.name);
  1200.                 
  1201.                 const s_colors = ["#FFCCCC", "#FFCC99", "#FFFF99", "#FFFFCC", "#99FF99",
  1202.                                   "#99FFFF", "#CCFFFF", "#CCCCFF", "#FFCCFF", "#FF6666",
  1203.                                   "#FF9966", "#FFFF66", "#FFFF33", "#66FF99", "#33FFFF",
  1204.                                   "#66FFFF", "#9999FF", "#FF99FF", "#FF0000", "#FF9900",
  1205.                                   "#FFCC66", "#FFFF00", "#33FF33", "#66CCCC", "#33CCFF",
  1206.                                   "#6666CC", "#CC66CC", "#CC0000", "#FF6600", "#FFCC33",
  1207.                                   "#FFCC00", "#33CC00", "#00CCCC", "#3366FF", "#6633FF",
  1208.                                   "#CC33CC", "#990000", "#CC6600", "#CC9933", "#999900",
  1209.                                   "#009900", "#339999", "#3333FF", "#6600CC", "#993399",
  1210.                                   "#660000", "#993300", "#996633", "#666600", "#006600",
  1211.                                   "#336666", "#000099", "#333399", "#663366", "#330000",
  1212.                                   "#663300", "#663333", "#333300", "#003300", "#003333",
  1213.                                   "#000066", "#330099", "#330033"];
  1214.                 assureDefault("color", s_colors[(new Date()).getUTCMilliseconds() % s_colors.length]);
  1215.             }
  1216.         }
  1217.         catch (exc) { // never break the listener chain
  1218.             this.notifyError(exc);
  1219.         }
  1220.     },
  1221.     
  1222.     // called before the unregister actually takes place
  1223.     onCalendarUnregistering: function calWcapSession_onCalendarUnregistering(cal)
  1224.     {
  1225.         try {
  1226.             // make sure the calendar belongs to this session and is the default calendar,
  1227.             // then remove all subscribed calendars:
  1228.             cal = this.belongsTo(cal);
  1229.             if (cal && cal.isDefaultCalendar) {
  1230.                 getFreeBusyService().removeProvider(this);
  1231.                 getCalendarSearchService().removeProvider(this);
  1232.                 var registeredCalendars = this.getRegisteredCalendars();
  1233.                 for each (var regCal in registeredCalendars) {
  1234.                     try {
  1235.                         if (!regCal.isDefaultCalendar) {
  1236.                             getCalendarManager().unregisterCalendar(regCal);
  1237.                         }
  1238.                     }
  1239.                     catch (exc) {
  1240.                         this.notifyError(exc);
  1241.                     }
  1242.                 }
  1243.             }
  1244.         }
  1245.         catch (exc) { // never break the listener chain
  1246.             this.notifyError(exc);
  1247.         }
  1248.     },
  1249.     
  1250.     // called before the delete actually takes place
  1251.     onCalendarDeleting: function calWcapSession_onCalendarDeleting(cal)
  1252.     {
  1253.     }
  1254. };
  1255.  
  1256. var g_confirmedHttpLogins = null;
  1257. function confirmInsecureLogin(uri)
  1258. {
  1259.     if (!g_confirmedHttpLogins) {
  1260.         g_confirmedHttpLogins = {};
  1261.         var confirmedHttpLogins = getPref(
  1262.             "calendar.wcap.confirmed_http_logins", "");
  1263.         var tuples = confirmedHttpLogins.split(',');
  1264.         for each (var tuple in tuples) {
  1265.             var ar = tuple.split(':');
  1266.             g_confirmedHttpLogins[ar[0]] = ar[1];
  1267.         }
  1268.     }
  1269.     
  1270.     var bConfirmed = false;
  1271.     
  1272.     var host = uri.hostPort;
  1273.     var encodedHost = encodeURIComponent(host);
  1274.     var confirmedEntry = g_confirmedHttpLogins[encodedHost];
  1275.     if (confirmedEntry) {
  1276.         bConfirmed = (confirmedEntry == "1");
  1277.     }
  1278.     else {
  1279.         var prompt = getWindowWatcher().getNewPrompter(null);
  1280.         var out_dontAskAgain = { value: false };
  1281.         var bConfirmed = prompt.confirmCheck(
  1282.             calGetString("wcap", "noHttpsConfirmation.label"),
  1283.             calGetString("wcap", "noHttpsConfirmation.text", [host]),
  1284.             calGetString("wcap", "noHttpsConfirmation.check.text"),
  1285.             out_dontAskAgain);
  1286.  
  1287.         if (out_dontAskAgain.value) {
  1288.             // save decision for all running calendars and
  1289.             // all future confirmations:
  1290.             var confirmedHttpLogins = getPref("calendar.wcap.confirmed_http_logins", "");
  1291.             if (confirmedHttpLogins.length > 0)
  1292.                 confirmedHttpLogins += ",";
  1293.             confirmedEntry = (bConfirmed ? "1" : "0");
  1294.             confirmedHttpLogins += (encodedHost + ":" + confirmedEntry);
  1295.             setPref("calendar.wcap.confirmed_http_logins", "CHAR", confirmedHttpLogins);
  1296.             getPref("calendar.wcap.confirmed_http_logins"); // log written entry
  1297.             g_confirmedHttpLogins[encodedHost] = confirmedEntry;
  1298.         }
  1299.     }
  1300.  
  1301.     log("returned: " + bConfirmed, "confirmInsecureLogin(" + host + ")");
  1302.     return bConfirmed;
  1303. }
  1304.  
  1305.